`timescale 1ns / 1ps
// ********************************************************************
//	FileName	: Debug_core.v
//	Author		：hpy 
//	Email		：yuan_hp@qq.com 
//	Date		：2020年12月20日 
//	Description	：串口调试的顶层文件，根据时钟频率和需要的串口波特率，参数
//  可自动完成配置，当解析得到 cmd和cmd_data时，valid_o输出一个clk的高电平，
//  这时读取数据是比较靠谱的。
//  串口其他参数：
//      数据位：8
//      起始位：0
//      停止位：1
//      无奇偶校验位
// -------------------------------------------------------------------- 
/*--------------- 例化模板 ------------------
Debug_core #( 
    .CLK_FREQ (12000000),    //输入时钟频率  clk
    .BSP( 115200)            // 波特率
) Debug_uut(
    .clk(),
    .rst_n(),
    .wr_busy(),   //写数据忙 忙==> 1
    .wr(),        //写使能 ,一个clk的高电平时间
    .wr_data(),   //写的数据，[31:0]
    .valid_o(),    //得到解码有效的数据，产生一个clk的高电平
    .cmd(),        //解析得到的指令编号
    .cmd_data(),   //解析得到的指令数据
    .rx_valid(),   //串口接收到一个数据，产生一个clk的高电平
    .rx_data(),    //串口读取到的数据
    .rx(), 
    .tx()
);
-------------------------------------------*/


//*************************************************************************************************
module Debug_core_uart_rx#( 
    parameter CLK_FREQ = 12000000, //输入时钟
    parameter BSP = 115200 // 波特率
)(
    input rst_n,  
    input clk, 
	input rx ,				//UART串行数据输入
    output [ 7 : 0] rdata, 	//UART数据接收接收到的数据
    output busy, 			//UART数据接收模块接收数据忙    
	output interrupt, 		//数据接收完毕中断          
    output err 				//UART数据接收帧出错
    
);
    localparam CNT_MAX = CLK_FREQ / BSP;  		//根据时钟频率和波特率计算分频值
	localparam CNT_MAX_HALF = (CNT_MAX>>1) - 1; //在中间点采样，增加数据可靠性

    function  integer clog2(input integer din) ;
        for(clog2=0;din;clog2=clog2+1)
            din=din>>1; 
    endfunction

    reg r_uart_rx_buf;
    reg r_uart_rx_falling;
    
    reg [clog2(CNT_MAX) - 1 : 0] r_sample_cnt;  //采样计数器

    reg [1 : 0] cstate, nstate;
    parameter [1 : 0] idle = 2'b00;
    parameter [1 : 0] receive_data = 2'b01;
    parameter [1 : 0] receive_done = 2'b10;
    
    reg [3 : 0] r_shift_cnt;
    reg [9 : 0] r_shift;
    
    reg [7 : 0] r_rx_data;
    reg r_uart_rx_err;
    
    assign rdata = r_rx_data;
    assign busy = (cstate == idle)? 1'b0 : 1'b1;
    assign err = r_uart_rx_err;

    //assign r_uart_rx_falling = ~rx & (r_uart_rx_buf);

//----------------------------- 检rx的下降沿 ---------------------------   
    always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
        begin
            r_uart_rx_buf <= 1'b0;
            r_uart_rx_falling <= 1'b0;
        end
        else
        begin
            r_uart_rx_buf <= rx;
			r_uart_rx_falling <= ~rx & (r_uart_rx_buf);
        end 
    end
    

//------------------------- UART下个状态到现状态的转化 --------------------------
  
    always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
            cstate <= idle;
        else
            cstate <= nstate;
    end
    
    
//----------------------------- UART下个状态的转化  -----------------------------  
    always @(*) 
    begin
        case(cstate)
            idle        :
                if(1'b1 == r_uart_rx_falling)
                    nstate = receive_data;
                else
                    nstate = idle;            
            receive_data  :
                if(4'd10 == r_shift_cnt)
                    nstate = receive_done;
                else
                    nstate = receive_data;
            receive_done     : 
                    nstate = idle;
            default     :
                    nstate = idle;
         endcase
    end
    
//------------------------------- UART在现状态的操作 ----------------------------
always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
        begin
            r_shift <= 10'b1111111111;
            r_shift_cnt <= 4'd0;
            r_sample_cnt <= 'd0;
            r_rx_data <= 8'd0;
            r_uart_rx_err <= 1'b0;
        end
        else
        begin
				case(cstate)
					receive_data:begin
						 if(CNT_MAX_HALF == r_sample_cnt)
						 begin
							  r_shift_cnt <= r_shift_cnt + 4'd1;
							  r_shift <= {rx, r_shift[9 : 1]};
							 // r_sample_cnt <= r_sample_cnt + 4'd1;
						 end

						if(r_sample_cnt == CNT_MAX - 1'b1)
								r_sample_cnt <= 'd0;
						 else
								r_sample_cnt <= r_sample_cnt + 1'b1;
					end 
					receive_done:begin
						 r_shift_cnt <= 4'd0;
						 r_shift <= 10'b1111111111;
						 r_rx_data <= r_shift[8 : 1];
						 if(1'b0 == r_shift[9])
							  r_uart_rx_err <= 1'b1;
						 else
							  r_uart_rx_err <= 1'b0;
						 r_shift_cnt <= 4'd0;
						 r_sample_cnt <=r_sample_cnt + 1'b1;
					end 
					idle :begin
					    if(r_uart_rx_falling && (r_sample_cnt >CNT_MAX - 1 ))begin 
							  r_sample_cnt <= 'd0;
						 end 
						 else if(r_sample_cnt < CNT_MAX - 1 ) begin
							  r_sample_cnt <= r_sample_cnt + 1'b1;
						 end
					end 
					
					default:begin
						 r_shift <= 10'b1111111111;
						 r_shift_cnt <= 4'd0;
						 r_sample_cnt <= 'd0;
					end 
				endcase 
//				
//            if(receive_data == cstate) begin
//                if(CNT_MAX_HALF == r_sample_cnt)
//                begin
//                    r_shift_cnt <= r_shift_cnt + 4'd1;
//                    r_shift <= {rx, r_shift[9 : 1]};
//                   // r_sample_cnt <= r_sample_cnt + 4'd1;
//                end
//
//					if(r_sample_cnt == CNT_MAX - 1'b1)
//						r_sample_cnt <= 'd0;
//					 else
//						r_sample_cnt <= r_sample_cnt + 1'b1;
//					end
//            else if(receive_done == cstate)
//            begin
//                r_shift_cnt <= 4'd0;
//                r_shift <= 10'b1111111111;
//                r_rx_data <= r_shift[8 : 1];
//                if(1'b0 == r_shift[9])
//                    r_uart_rx_err <= 1'b1;
//                else
//                    r_uart_rx_err <= 1'b0;
//                r_shift_cnt <= 4'd0;
//                r_sample_cnt <=r_sample_cnt + 1'b1;
//            end  
//            else if( idle == cstate) begin 
//                if(r_uart_rx_falling)begin 
//                    r_sample_cnt <= 'd0;
//					 end 
//                else if(r_sample_cnt < CNT_MAX - 1 ) begin
//                    r_sample_cnt <= r_sample_cnt + 1'b1;
//                end
//            end               
//            else
//            begin
//                r_shift <= 10'b1111111111;
//                r_shift_cnt <= 4'd0;
//                r_sample_cnt <= 'd0;
//            end          
        end
    end

//------------------------------- 中断信号生成 -----------------------------
	reg busy_f;
	assign interrupt = (~busy) & busy_f;
	always @ (posedge clk , negedge rst_n) begin
		if( rst_n == 1'b0) begin
			busy_f <= 1'b0;
		end
		else begin
			busy_f<=busy;
		end
	end
    
    	    
endmodule


//*************************************************************************************************
module Debug_core_uart_tx #( 
    parameter CLK_FREQ = 12000000, //输入时钟
    parameter BSP = 115200 // 波特率
)(
        input rst_n,
        input clk, 

        input start, //UART数据发送指令
        input [7 : 0] tdata, //UART发送的数据
        output busy, //UART发送模块忙  
        output  interrupt,    
        
        output tx //UART数据输出
    );
    
    reg r_tx_order_buf;
    wire r_tx_order_rising;
    
    localparam CNT_MAX = CLK_FREQ / BSP;  //根据时钟频率和波特率计算分频值
    localparam ERROR_D = 3 ; // 设置容错范围，这个参数只会影响回环测试，当不使用回环测试时，这个参数应当设置为3，但根据情况，设置为5在各种情况下可用

    function  integer clog2(input integer din) ;
        for(clog2=0;din;clog2=clog2+1)
            din=din>>1; 
    endfunction
    
    reg [ 1 : 0 ] cstate, nstate;
    parameter [1 : 0] idle = 2'b00;
    parameter [1 : 0] load_data = 2'b01;
    parameter [1 : 0] shift_data = 2'b10;
    
    reg [3 : 0] r_shift_cnt;
    reg [12 : 0] r_shift;
    reg [clog2(CNT_MAX) - 1 : 0] r_hold_cnt;
    
    assign tx = r_shift[0];
    assign busy = (nstate == idle)? 1'b0 : 1'b1;


// ---------------------- 检测发送上升沿 ------------------------
	assign r_tx_order_rising = start & (~r_tx_order_buf);  
    always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
        begin
            r_tx_order_buf <= 1'b0;
            //r_tx_order_rising <= 1'b0;
        end
        else
        begin
            r_tx_order_buf <= start;
            //r_tx_order_rising <= start & (~r_tx_order_buf);
        end
    end
    
//---------------------- UART下个状态转化到现状态  ----------------------   
    always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
            cstate <= idle;
        else
            cstate <= nstate;
    end
    
    

//---------------------------------UART下个状态转化进程 ------------------------------  
    always @(*) 
    begin
        case(cstate)
            idle        :
                if(1'b1 == r_tx_order_rising)
                    nstate = shift_data;
                else
                    nstate = idle;
            shift_data  :
                if(4'd10 == r_shift_cnt)
                    nstate = idle;
                else 
					nstate = shift_data;
            default     :
                    nstate = idle;
         endcase
    end
    
// --------------------------  UART在各个现状态下的操作  -----------------------------  
    always @(posedge clk, negedge rst_n) 
    begin
        if(1'b0 == rst_n)
        begin
            r_shift <= 13'b1111111111111;
            r_shift_cnt <= 4'd0;
            r_hold_cnt <= 'd0;
        end
        else
        begin
            if(idle == cstate && 1'b1 == r_tx_order_rising)
            begin
                r_shift_cnt <= 4'd0;
                r_shift <= {4'b1111, tdata, 1'b0};
                r_hold_cnt <=0;
            end                
            else if(shift_data == cstate)
            begin
				if(  r_shift_cnt==4'd9  ) begin   //消除idel状态带入的延时,同时缩短停止位时间，实现可根据busy信号连续发送数据和回环测试
					if( (CNT_MAX - 3) == r_hold_cnt)
					begin
						r_shift_cnt <= r_shift_cnt + 4'd1;
						r_shift <= {1'b1, r_shift[12 : 1]};
						r_hold_cnt <= 'd0;
					end        
					else  r_hold_cnt <= r_hold_cnt + 4'd1;  
				end
				else begin
					if( (CNT_MAX - 1'b1) == r_hold_cnt)
					begin
						r_shift_cnt <= r_shift_cnt + 4'd1;
						r_shift <= {1'b1, r_shift[12 : 1]};
						r_hold_cnt <= 'd0;
					end        
					else  r_hold_cnt <= r_hold_cnt + 4'd1; 					
				end
            end 
            else
            begin
                r_shift <= 10'b1111111111;
                r_shift_cnt <= 4'd0;
				r_hold_cnt <=0;
            end          
        end
    end

    reg busy_f;
    assign interrupt = (~busy) & busy_f;
	always@(posedge clk or negedge rst_n) begin
		if( !rst_n) 
			busy_f <= 1'b0;
		else begin
            busy_f <= busy;
		end

	end
    	    
endmodule




//***************************************************************************************************

module Debug_core_DecodeUart(
    input clk,
    input rst_n,
    input valid_i,           //当高电平时，输入的数据有效
    input [7:0] din,        //串口发来的数据
    output reg [7:0] cmd,   //解码得到的指令编号
    output reg [31:0] data, //解码得到的指令数据段
    output reg valid_o      //解码数据有效信号，当这个信号为高电平时，cmd和data数据有效
);

//---------------- 指令头尾定义 -----------------
localparam 
    START = 8'HAA, //指令头
    TAIL  = 8'h55; //指令尾  
//----------------- 状态机状态定义 ---------------
localparam
    IDEL = 0,
    S1 = 1;

//---------------- 定义一些辅助寄存器-------------
reg cst , nst ;//状态机当前状态和下一状态 
reg [2:0] cnt; //接收数据计数器

//---------------- 状态机进入下一个状态 ----------
always @ (posedge clk , negedge rst_n) begin
    if(rst_n == 1'b0)
        cst <= IDEL;
    else
        cst <= nst ;
end

//---------------- 状态切换 ---------------------
always @( * ) begin
   case(cst)
        IDEL : nst =(valid_i && (din==START)) ? S1 : IDEL;
        S1: nst = (cnt==3'd7)? IDEL : S1 ;
        default : nst = IDEL;
   endcase 
end

//----------------- 输出解析的指令 ----------------
always @(posedge clk , negedge rst_n) begin
    if( rst_n == 1'b0 ) begin
        cnt <= 3'd0;
        valid_o <= 1'b0;
        cmd <= 8'd0;
        data <= 32'd0;
    end
    else begin
        case(nst)
            IDEL: begin 
                valid_o <= 1'b0;
                cnt <= 3'd0;
            end
            S1: begin
                if(valid_i) begin
                    if(cnt == 3'd1) cmd <= din;
                    if(cnt == 3'd2) data[31:24] <= din;
                    if(cnt == 3'd3) data[23:16] <= din;
                    if(cnt == 3'd4) data[15:8]  <= din;
                    if(cnt == 3'd5) data[7:0]   <= din;
                    if( (cnt == 3'd6) && (din==TAIL) ) valid_o <= 1'b1; 
                    cnt <= cnt + 1'b1;
                end
            end
            default : ;
        endcase
    end
end

endmodule

//******************************************************************************************************

module Debug_core #( 
    parameter CLK_FREQ = 12000000, //输入时钟
    parameter BSP = 115200 // 波特率
)(
    input clk,
    input rst_n,
    output wr_busy,  //写数据忙 忙==> 1
    input wr,        //写使能  
    input [31:0]wr_data,
    output valid_o,
    output [7:0] cmd,
    output [31:0] cmd_data,
    output rx_valid,      //串口接收到一个数据，产生一个clk的高点平
    output [7:0] rx_data, //串口读取到的数据
    input rx,
    output tx 
);

reg [2:0] cnt;  //计数器

wire tx_busy,tx_ok;
reg start;
reg [7:0]tdata;

wire [7:0]rdata;
wire valid_i;

assign wr_busy = (cnt==3'd0) ? 1'b0 : 1'b1;
assign rx_valid = valid_i;
assign rx_data = rdata;
reg [31:0] tx_buf;
always @(posedge clk , negedge rst_n ) begin
    if(!rst_n)begin
        start <= 1'b1;
        tx_buf <= 32'd0;
        cnt <= 3'd0;
    end
    else begin
        if( cnt > 3'd1 ) begin
            if( (!start) && (!tx_busy)) begin
                start <= 1'b1;
                tdata <= tx_buf[31:24];
                tx_buf <= tx_buf<<8;
            end
            else start <= 1'b0;
        end
        else begin
            if(wr) begin tx_buf <= wr_data; cnt <= 3'd4; end 
            start <= 1'b0;
        end

        //发送完毕将数据减去1
        if(tx_ok) begin
            cnt <= (cnt==3'd0)? 3'd0 : cnt - 1'b1;
        end
    end
end

Debug_core_uart_rx #( 
	.CLK_FREQ(CLK_FREQ),   //时钟频率
	.BSP(BSP)   // 波特率
) Debug_core_uart_rx_uut ( 
	.clk(   clk),
	.rst_n( rst_n),
	.rx(    rx) , 		// 串口接收数据线
	.rdata( rdata),  		// 接收到的数据
	.interrupt( valid_i)
);

Debug_core_DecodeUart Debug_core_DecodeUart_uut(
    .clk(   clk),
    .rst_n( rst_n),
    .valid_i(   valid_i),         //当高电平时，输入的数据有效
    .din(   rdata),             //串口发来的数据
    .cmd(   cmd),             //解码得到的指令编号
    .data(  cmd_data),            //解码得到的指令数据段
    .valid_o(   valid_o)          //解码数据有效信号，当这个信号为高电平时，cmd和data数据有效
);

Debug_core_uart_tx #( 
    .CLK_FREQ(CLK_FREQ),    //输入时钟频率
    .BSP(BSP)               // 波特率
) Debug_core_uart_tx_uut ( 
    .rst_n( rst_n),
    .clk(   clk),
    .start( start),     //UART数据发送指令
    .tdata( tdata),     //UART发送的数据
    .busy(  tx_busy),    //UART发送模块忙  
    .interrupt( tx_ok), // 当数据接发送完毕会产生一个clk的高电平
    .tx(    tx)         //UART数据输出
);

endmodule
 